package des.handling;

import java.io.UnsupportedEncodingException;

import des.algorithm.DES;
import des.algorithm.Encryption;
import des.algorithm.TripleDES;
import des.algorithm.Encryption.EncryptionType;

public class TextHandler extends Handler {
	protected static EncryptionType encryptionType;

	public static String encryptText(String s, long[] keys, EncryptionType type) {

		encryptionType = type;
		status = Status.ENCRYPTING;
		return handleText(s, keys);

	}

	public static String decryptText(String s, long[] keys, EncryptionType type) {

		encryptionType = type;
		status = Status.DECRYPTING;
		return handleText(s, keys);
	}

	private static String handleText(String s, long[] keys) {
		//long startTime = System.currentTimeMillis();

		Encryption encryptor;

		if (encryptionType == EncryptionType.DES) {
			encryptor = new DES(keys[0]);
		} else {
			encryptor = new TripleDES(keys[0], keys[1]);
		}

		long[] srcLongs;

		if (status == Status.ENCRYPTING) {
			srcLongs = fromStringToLongs(s);
		} else {
			srcLongs = fromHexStringToLongs(s);
		}

		long[] trgLongs = new long[srcLongs.length];

		for (int i = 0; i < srcLongs.length; i++) {
			if (status == Status.ENCRYPTING) {
				trgLongs[i] = encryptor.encryptLong(srcLongs[i]);
				// trgLongs[i] = srcLongs[i];
			} else {
				trgLongs[i] = encryptor.decryptLong(srcLongs[i]);
				// trgLongs[i] = srcLongs[i];
			}
			
		}

		String res;

		if (status == Status.ENCRYPTING) {
			res = fromLongsToHexString(trgLongs);
		} else {
			res = fromLongsToString(trgLongs);
		}

		//long endTime = System.currentTimeMillis();
		return res;

	}

	private static String fromLongsToString(long[] trgLongs) {
		StringBuilder sb = new StringBuilder();
		for (long l : trgLongs) {
			System.out.println(Long.toHexString(l));
			byte[] bytes = new byte[8];
			for (int i = 7; i >= 0; i--) {
				bytes[i] = (byte) (l & 0xFFL);

				l = l >> 8;

			}

			String s = new String(bytes);

			sb.append(s);

		}
		
		return sb.toString();
	}

	static String fromLongsToHexString(long[] longs) {
		StringBuilder s = new StringBuilder(longs.length * 8);

		for (long l : longs) {
			String temp = Long.toHexString(l);
			
			if (temp.length() == 16) {
				s.append(temp);
			} else {
				int nrMissing = 16-temp.length();
				for (int i = 0; i < nrMissing; i++) {
					temp = "0" + temp;
				}
				s.append(temp);
				
			}
			System.out.println(Long.toHexString(l));
		}
		return s.toString();
	}

	static long[] fromHexStringToLongs(String s) {
		int n = s.length();
		int nLongs = (n / 16);
		long[] longs = new long[nLongs];


		for (int i = 0; i < nLongs; i++) {
			String firstHalf = s.substring(i*16, (i*16)+8);
			String secondHalf = s.substring((i*16)+8, (i*16)+16);
	
			longs[i] = (Long.parseLong(firstHalf,16)<<32) | Long.parseLong(secondHalf,16);
			System.out.println(Long.toHexString(longs[i]));
		}
		
		return longs;
	}

	static long[] fromStringToLongs(String s) {
		byte[] bytes = null;
		try {
			bytes = s.getBytes("ISO-8859-1");
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		long l = 0l;
		int sPos, n;
		n = bytes.length;

		int nLongs = (n / 8) + 1;
		int iLongs = 0;

		long[] longs = new long[nLongs];

		sPos = 0;

		while (n > 0) {
			for (int i = sPos; i <= sPos + 7; i++) {
				try {
					l = l << 8;
					l = l | (bytes[i] & 0xFFL);
				} catch (IndexOutOfBoundsException ioofbEx) {
					// ioofbEx.printStackTrace();
				}
			}
			sPos += 8;
			n -= 8;

			longs[iLongs] = l;
			iLongs++;
		}
		
		for (long l1: longs) {
			System.out.println(Long.toHexString(l1));
		}
		
		
		return longs;
	}

	public static long createKeyFromPassword(String password) {
		byte[] pwbytes = password.getBytes();
		long key = 0x0l;
		for (int i = 0; i < 8; i++) {
			if (i < pwbytes.length) {
				byte b = pwbytes[i];
				// flip the byte
				byte b2 = 0;
				for (int j = 0; j < 8; j++) {
					b2 <<= 1;
					b2 |= (b & 0x01);
					b >>>= 1;
				}
				key = key << 8;
				key = key | (b2 & 0xFF);
			}
		}

		return key;
	}

}
